| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- import { notFound } from "next/navigation";
- import { Suspense } from "react";
- // import clsx from "clsx";
- import {
- ProductDetailSkeleton,
- RelatedProductSkeleton,
- } from "@/components/common/skeleton/ProductSkeleton";
- import {
- BASE_SCHEMA_URL,
- PRODUCT_TYPE,
- } from "@/utils/constants";
- import { GET_PRODUCT_BY_URL_KEY } from "@/graphql";
- import { cachedProductRequest } from "@/utils/hooks/useCache";
- import {
- ProductOption,
- SingleProductResponse,
- ProductMediaType,
- ProductFlexibleVariant,
- ResolvedVariant
- } from "@components/catalog/type";
- import { RelatedProductsSection } from "@components/catalog/product/RelatedProductsSection";
- import { HeroCarouselShimmer } from "@components/common/slider";
- import {ProductMedia} from "@/app/(public)/product/_components/ProductMedia";
- import { ProductInformation } from "@/app/(public)/product/_components/ProductInformation";
- import { ProductShortDescription } from "@/app/(public)/product/_components/ProductShortDescription";
- import { ProductDetail } from "@/app/(public)/product/_components/ProductDetail";
- import { ProductReviewSection } from "@/app/(public)/product/_components/ProductReviewSection";
- import {
- formatFlexibleVariants
- } from "@/utils/variantTools";
- // export const dynamic = 'auto'
- // // 'auto' | 'force-dynamic' | 'error' | 'force-static'
- async function getSingleProduct(urlKey: string) {
- try {
- const {data:dataById} = await cachedProductRequest<SingleProductResponse>(
- urlKey, // 产品名称
- GET_PRODUCT_BY_URL_KEY, // gql查询语句
- { urlKey: urlKey },
- );
- const product = dataById?.product || null;
- return product;
- } catch (error) {
- if (error instanceof Error) {
- console.error("Error fetching product:", {
- message: error.message,
- urlKey,
- graphQLErrors: (error as unknown as Record<string, unknown>)
- .graphQLErrors,
- });
- }
- return null;
- }
- }
- //动态路由中的参数 -- params
- export default async function ProductPage({
- params,
- }: {
- params: Promise<{ urlProduct: string[] }>;
- searchParams: Promise<{ type: string }>;
- }) {
- const { urlProduct } = await params;
- const fullPath = urlProduct.join("/");
- const product = await getSingleProduct(fullPath);
- if (!product) return notFound();
- // const imageUrl = getImageUrl(product?.baseImageUrl, baseUrl, NOT_IMAGE);
- // JSON-LD数据格式,便于搜索引擎识别页面信息,利于seo
- const productJsonLd = {
- "@context": BASE_SCHEMA_URL,
- "@type": PRODUCT_TYPE,
- name: product?.name,
- description: product?.description,
- sku: product?.sku,
- };
- const mediaImgs = (product?.images?.edges ?? []).map((edge: { node: ProductMediaType }) => {
- return edge.node;
- });
- const productOptions: ProductOption[] = product?.productOptions ? JSON.parse(product?.productOptions) : [];
- const flexibleVariants = formatFlexibleVariants(product?.flexibleVariants);
- const reviews = Array.isArray(product?.reviews?.edges)
- ? product?.reviews.edges.map((e) => e.node)
- : [];
- // const VariantImages = isArray(product?.variants?.edges)
- // ? product?.variants.edges.map(
- // (edge: { node: ProductVariantNode }) => edge.node,
- // )
- // : [];
- return (
- <>
- <script //SEO
- dangerouslySetInnerHTML={{
- __html: JSON.stringify(productJsonLd),
- }}
- type="application/ld+json"
- />
- <div className="w-full">
- <Suspense fallback={<HeroCarouselShimmer />}>
- <ProductMedia mediaData={mediaImgs} />
- </Suspense>
- </div>
-
- <div className="">
- <Suspense fallback={<ProductDetailSkeleton />}>
- <ProductInformation key={product._id}
- productId={product._id}
- name={product?.name || ''}
- productOptions={productOptions}
- flexibleVariants={flexibleVariants}
- isSaleable={product.isSaleable}
- />
- <ProductShortDescription shortDescription={product.shortDescription || ''} />
- <ProductDetail detail={product.description || ''} />
- <ProductReviewSection productId={String(product._id)} />
- </Suspense>
- </div>
- <Suspense fallback={<RelatedProductSkeleton />}>
- <RelatedProductsSection fullPath={fullPath} />
- </Suspense>
- </>
- );
- }
|